import wx
from random import randint as rand

DEFAULT_WIDTH = 20
DEFAULT_HEIGHT = 15

THROBBER_EVENT = wx.NewEventType()
EVT_UPDATE_THROBBER = wx.PyEventBinder(THROBBER_EVENT, 0)

DRAWING_LIST = ['striking', 'full', 'random']

CRLIST = [wx.GREEN, wx.BLUE]#, wx.GREEN, wx.WHITE, wx.BLACK]

class UpdateThrobberEvent(wx.PyEvent):
    def __init__(self):
        wx.PyEvent.__init__(self)
        self.SetEventType(THROBBER_EVENT)

class Img(wx.PyPanel):
    """ derived from wx.PyPanel which allow you override more
    common C++ virtual methods.
    """
    def __init__(self, parent, id,
                 imgtype, # the real drawing scheme class
                 pos = wx.DefaultPosition,
                 size = wx.DefaultSize,
                 frameDelay = 0.1,  # delay between two draw (in seconds)
                 style = 0,
                 name='Img'):
        wx.PyPanel.__init__(self, parent, id, pos, size,style,name)

        # set size, or set to default_[WIDTH,HEIGHT]
        width, height = size
        if width == -1:
            width = DEFAULT_WIDTH
        if height == -1:
            height = DEFAULT_HEIGHT
        self.width, self.height = width,height
        assert width != -1 and height != -1, "given wrong size!"
        imgtype.panelsize = (width,height)
        self.imgtype = imgtype
        self.frameDelay = frameDelay
        self.running = (1 != 1)
        self.SetClientSize((width,height))


        # inner timer
        timerID = wx.NewId()
        self.timer = wx.Timer(self, timerID)

        # event binds
        self.Bind(EVT_UPDATE_THROBBER, self.Update)
        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_TIMER, self.OnTimer, self.timer)
        self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroyWindow)
        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
        self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
        self.Bind(wx.EVT_SIZE, self.OnSize)

        # Disable background erasing (flicker-licious)
        def disable_event(*pargs,**kwargs):
            pass # the sauce, please
        self.Bind(wx.EVT_ERASE_BACKGROUND, disable_event)
        self.OnSize(None)

    # override method for Sizer
    def DoGetBestSize(self):
        return (self.width, self.height)

    # timer to trigger draw
    def OnTimer(self, event):
        # append a THROBBER_EVENT to self
        # on event THROBBER_EVENT , self.Update(event) is called
        # which will call Draw
        wx.PostEvent(self, UpdateThrobberEvent())

    # mouse control
    def OnLeftDown(self, event):
        pt = event.GetPosition()
        self.Toggle()
    def OnRightDown(self, event):
        dlg = ConfImg(None, -1, self, "Sample Dialog", size=(350, 200),
                         #style=wx.CAPTION | wx.SYSTEM_MENU | wx.THICK_FRAME,
                         style=wx.DEFAULT_DIALOG_STYLE, # & ~wx.CLOSE_BOX,
                         )
        # this does not return until the dialog is closed.
        if dlg:
            val = dlg.ShowModal()
            dlg.Destroy()        

    def OnDestroyWindow(self, event):
        self.Stop()
        event.Skip()

    def OnPaint(self, event):
        dc = wx.BufferedPaintDC(self, self.buffer)
        event.Skip()

    def OnSize(self, event):
        width, height = self.GetClientSizeTuple()
        if width == 0:
            width = 1
        if height == 0:
            height = 1
        self.buffer = wx.EmptyBitmap(width,height)
        self.backbuffer = wx.EmptyBitmap(width,height)
        
    def _flip(self):
        self.buffer,self.backbuffer = self.backbuffer, self.buffer
        self.Refresh()
    
    def Update(self, event):
        """ not in paint, so use ClientDC"""
#        self.Draw(wx.ClientDC(self))
        dc = wx.MemoryDC()
        dc.SelectObject(self.backbuffer)
        dc.BeginDrawing()
        self.Draw(dc)
        dc.EndDrawing()
        self._flip()

    def Draw(self, dc):
        """Draw some pixels"""
        self.imgtype.draw(dc)
        pass

    class DrawRandom(object):
        def draw(self, pdc):
            dc = pdc
            rect = wx.Rect(0,0, 60, 60)
            for RGB, pos in [((rand(0,255),  34,  34), ( 0,  0)),
                         (( 35, rand(0,255),  35), (55, 50)),
                         ((  0,   0, rand(0,255)), (70,  0))
                         ]:
                r, g, b = RGB
                penclr   = wx.Colour(r, g, b, wx.ALPHA_OPAQUE)
                brushclr = wx.Colour(r, g, b, 128)   # half transparent
                dc.SetPen(wx.Pen(penclr))
                dc.SetBrush(wx.Brush(brushclr))
                rect.SetPosition(pos)
                dc.DrawRoundedRectangleRect(rect, 8)
    
    class DrawStriking(object):
        def __init__(self, crlist, width=5, step=1, reverse=False, hori=True):
            self.crlist = crlist
            self.width = width
            self.step = step
            self.reverse = reverse
            self.hori = hori
            self.index = 0
            self.panelsize = (50,50)
        def draw(self, pdc):
            dc = pdc
            rect = wx.Rect(0,0, self.width, self.panelsize[1])
            i = self.index  ## should always from left of the screen
            while i < self.panelsize[0]:
                for RGB in self.crlist:
                    penclr = RGB
                    brushclr = RGB
                    dc.SetPen(wx.Pen(penclr))
                    dc.SetBrush(wx.Brush(brushclr))                    
                    rect.SetPosition((i, 0))
                    x0,y0,x1,y1 = rect.x,rect.y, rect.width,rect.height
                    dc.DrawRectangle(x0,y0,x1,y1)
                    i += self.width
                
            if self.reverse:
                self.index -= self.step
            else:
                self.index += self.step
            if self.index + len(self.crlist)*self.width < 0:
                self.index += len(self.crlist)*self.width
            if self.index > 0:
                self.index -= len(self.crlist)*self.width
                
    def Start(self):
        """Start the animation"""
        if not self.running:
            self.running = not self.running
            self.timer.Start(int(self.frameDelay * 1000))

    def Stop(self):
        """End the animation"""
        if self.running:
            self.timer.Stop()
            self.running = not self.running

    def Toggle(self):
        """Toggle timer on/off"""
        if self.running:
            self.Stop()
        else:
            self.Start()

    def IsRunning(self):
        return self.running

    def SetFrameDelay(self, frameDelay = 0.05):
        """ delay between each frame, default is 0.05 seconds"""
        self.frameDelay = frameDelay
        if self.running:
            self.Stop()
            self.Start()

    def SetImgtype(self, inttype):
        self.OnSize(None)
        if inttype == 0:
            self.imgtype = Img.DrawStriking(CRLIST, width=10,step=2)
        elif inttype == 1:
            self.imgtype = Img.DrawRandom()
        self.imgtype.panelsize = (self.width, self.height)
        
class ConfImg(wx.Dialog):
    def __init__(self, parent, id, img, title="Configuration of Picture",
                 size=wx.DefaultSize, pos=wx.DefaultPosition,
                 style=wx.DEFAULT_DIALOG_STYLE):
        if not type(img) is Img:
            return None
        # two step creation
        pre = wx.PreDialog()
        pre.SetExtraStyle(wx.DIALOG_EX_CONTEXTHELP)
        pre.Create(parent, id, title, pos, size, style)
        self.PostCreate(pre)

        # Now continue with the normal construction of the dialog
        # contents
        vs0 = wx.BoxSizer(wx.VERTICAL)

        hs1 = wx.BoxSizer(wx.HORIZONTAL)

        rb = wx.RadioBox(self, -1, "Drawing Type",
                         wx.DefaultPosition, wx.DefaultSize,
                         DRAWING_LIST, 1, wx.RA_SPECIFY_COLS)
        self.Bind(wx.EVT_RADIOBOX, self.onEvtRadioBox, rb)
        hs1.Add(rb, 0, wx.ALL, 20)        
    
        self.viewimg = Img(self, -1, Img.DrawStriking(CRLIST, width=10,step=2), size=(100,100),style=wx.BORDER)
        self.viewimg.Start()
        hs1.Add(self.viewimg, 0, wx.ALL, 20)

        vs0.Add(hs1, 0, wx.ALL, 5)
        
        line = wx.StaticLine(self, -1, size=(20,-1), style=wx.LI_HORIZONTAL)
        vs0.Add(line, 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.TOP, 5)

        btnsizer = wx.StdDialogButtonSizer()

        btn = wx.Button(self, wx.ID_OK)
        btn.SetDefault()
        btnsizer.AddButton(btn)

        btn = wx.Button(self, wx.ID_CANCEL)
        btnsizer.AddButton(btn)
        btnsizer.Realize()

        vs0.Add(btnsizer, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)

        self.SetSizer(vs0)
        vs0.Fit(self)
        
    def onEvtRadioBox(self, event):
        self.selecttype = event.GetInt()
        self.viewimg.SetImgtype(self.selecttype)

class DrawPanel(wx.Panel):

    """Draw a line to a panel."""
    def __init__(self, *karg, **kargs):
        wx.Panel.__init__(self, *karg, **kargs)
        wx.EVT_CLOSE(self, self.OnClose)
        self.imglist = []
        for i in range(16):
            imgob = Img.DrawStriking(CRLIST, width=10,step=2)
            img = Img(self, -1, imgob, size=(200,200), pos=((i%4)*202,(i/4)*202),
                      frameDelay=0.01,style=wx.BORDER)
            img.Start()
            self.imglist.append(img)
            
    def OnClose(self, evt):
        self.Show(False)
        self.Destroy()
        
app = wx.PySimpleApp(0)
frame = wx.Frame(None, -1, "Draw on Frame", style = wx.DEFAULT_FRAME_STYLE & ~wx.CAPTION)
mainp = DrawPanel(frame)
frame.Show(True)
frame.Maximize()
app.MainLoop()
